package com.ccp.dev.workflow.service;

import com.ccp.dev.core.basic.base.BaseService;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.basic.web.query.QueryFilter;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.RequestContext;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.system.model.SysUser;
import com.ccp.dev.system.service.SysUserService;
import com.ccp.dev.workflow.dao.BpmTaskExeDao;
import com.ccp.dev.workflow.dao.ProcessRunDao;
import com.ccp.dev.workflow.dao.TaskDao;
import com.ccp.dev.workflow.model.*;
import com.ccp.dev.workflow.model.bpm.BpmRunLog;
import com.ccp.dev.workflow.model.bpm.ProcessCmd;
import com.ccp.dev.workflow.model.bpm.ProcessTask;
import com.ccp.dev.workflow.service.bpm.BpmRunLogService;
import com.ccp.dev.workflow.service.bpm.thread.MessageUtil;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * ClassDescribe 任务转办代理 Service类
 * @author:
 * Date: 2019-07-25
 */


@Service
public class BpmTaskExeService extends BaseService<BpmTaskExe> {

    @Resource
    private BpmTaskExeDao bpmTaskExeDao;
    @Resource
    private ProcessRunService processRunService;
    @Resource
    private ProcessRunDao processRunDao;
    @Resource
    private TaskDao taskDao;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private BpmService bpmService;
    @Resource
    private TaskMessageService taskMessageService;
    @Resource
    private SysTemplateService sysTemplateService;
    @Resource
    private BpmRunLogService bpmRunLogService;
    @Resource
    private BpmPerformanceService bpmPerformanceService;
    @Resource
    private TaskOpinionService taskOpinionService;

    /**
     * 完成任务时更新转办任务的状态。
     *
     * @param taskId
     */
    public void complete(String taskId) {
        List<BpmTaskExe> list = getByTaskId(taskId);
        SysUser sysuer = (SysUser) ContextUtil.getCurrentUser();
        for (BpmTaskExe bpmTaskExe : list) {
            if (bpmTaskExe.getStatus().shortValue() == BpmTaskExe.STATUS_INIT) {
                bpmTaskExe.setExeTime(new Date());
                bpmTaskExe.setExeUserId(sysuer.getUserId());
                bpmTaskExe.setExeUserName(sysuer.getFullName());
                if (bpmTaskExe.getAssigneeId().equals(sysuer.getUserId())) {
                    bpmTaskExe.setStatus(BpmTaskExe.STATUS_COMPLETE);
                } else {
                    bpmTaskExe.setStatus(BpmTaskExe.STATUS_OTHER_COMPLETE);
                }
            }
            bpmTaskExeDao.update(bpmTaskExe);
        }
    }

    /**
     * 根据任务ID获得任务转办代理
     *
     * @param taskId  任务ID
     * @return
     */
    public List<BpmTaskExe> getByTaskId(String taskId) {
        return bpmTaskExeDao.queryByTaskId(taskId);
    }

    /**
     * 通过任务ID取消记录
     *
     * @param taskId
     */
    public void cancel(String taskId) {
        List<BpmTaskExe> list = getByTaskIdStatusInit(taskId);
        if (BeanUtils.isNotEmpty(list)) {
            for(BpmTaskExe bpmTaskExe:list){
                bpmTaskExe.setStatus(BpmTaskExe.STATUS_CANCEL);
                bpmTaskExeDao.update(bpmTaskExe);
            }
        }
    }

    /**
     * 根据任务ID和任务状态获取转办代理数据
     * @param taskId
     * @return
     */
    public List<BpmTaskExe> getByTaskIdStatusInit(String taskId) {
        List<BpmTaskExe> bpmTaskExe = bpmTaskExeDao.getByTaskIdStatus(taskId,BpmTaskExe.STATUS_INIT);
        return bpmTaskExe;
    }
    /**
     * 根据流程实例ID获取转办代理事宜
     * @param runId
     */
    public List<BpmTaskExe> queryByRunId(String runId) {
        return bpmTaskExeDao.queryByRunId(runId);
    }
    /**
     * 根据流程实例ID删除转办代理事宜
     * @param runId
     */
    public void delByRunId(String runId) {
        bpmTaskExeDao.delByRunId(runId);
    }

    /**
     * 添加任务转办或代理。
     *
     * <pre>
     * 	1.添加转办消息。
     *  2.修改任务类型为转办。
     *  3.删除产生该人的通知任务。
     *  4.将原来的任务意见修改成当前执行人，并添加备注。
     *  5.新增流程日志，operatorType=1
     *  5.添加一条新的意见，状态为待审批，执行人为转办的人。
     *  4.发送信息通知沟通发起人。
     * </pre>
     *
     * @param bpmTaskExe
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void assignSave(BpmTaskExe bpmTaskExe) throws Exception {
        try {
            bpmTaskExeDao.add(bpmTaskExe);
            String taskId = bpmTaskExe.getTaskId();
            Short assignType = bpmTaskExe.getAssignType();
            String assigneeId = bpmTaskExe.getAssigneeId();
            Short opinionStatus = TaskOpinion.STATUS_DELEGATE;
            Integer useType = 0;
            Integer agentUseType = 0;//被代理人的信息模板号
            ProcessRun processRun = processRunDao.getById(bpmTaskExe.getRunId());
            if (BpmTaskExe.TYPE_ASSIGNEE.equals(assignType)) {
                opinionStatus = TaskOpinion.STATUS_AGENT;
                useType = SysTemplate.USE_TYPE_AGENT;
                agentUseType = SysTemplate.USE_TYPE_NOTIFYOWNER_AGENT;
            } else if (BpmTaskExe.TYPE_TRANSMIT.equals(assignType)) {
                //添加用于流程统计的操作记录
                ProcessCmd processCmd = new ProcessCmd();
                processCmd.setSubject(processRun.getSubject());
                processCmd.setVoteAgree(TaskOpinion.STATUS_DELEGATE);
                bpmPerformanceService.addProcessRecord(taskId, processCmd);

                opinionStatus = TaskOpinion.STATUS_DELEGATE;
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("taskId", taskId);
                params.put("userId", assigneeId);
                params.put("description", opinionStatus.toString());
                params.put("updateTime", new Date());
                taskDao.updateTask(params);
                useType = SysTemplate.USE_TYPE_DELEGATE;

                // 如果是多级转办，需要更新状态
                List<BpmTaskExe> list = getByTaskIdStatusInit(taskId);
                if (BeanUtils.isNotEmpty(list) && list.size() > 1) {
                    for (BpmTaskExe taskExe : list) {
                        taskExe.setAssignType(BpmTaskExe.TYPE_MUTIL_ASSIGNEE);
                        bpmTaskExeDao.update(taskExe);
                    }
                }
            }
            //取消通知任务，先判断是否存在这条数据，若存在再删除外键关联数据，再删除该条数据
            String actRuIdentitylinkTaskId = taskDao.getCommuTaskByInstNodeUser(bpmTaskExe.getProcInstId(), bpmTaskExe.getTaskDefKey(), assigneeId);
            if (actRuIdentitylinkTaskId != null && actRuIdentitylinkTaskId != "") {
                taskDao.delActRuIdentitylink(actRuIdentitylinkTaskId);
                taskDao.delCommuTaskByInstNodeUser(bpmTaskExe.getProcInstId(), bpmTaskExe.getTaskDefKey(), assigneeId);
            }
            // 将原来的任务审批意见修改成代办状态，并填写任务意见。
            TaskOpinion taskOpinion = taskOpinionService.getByTaskId(taskId);
            taskOpinion.setCheckStatus(opinionStatus);

            // 如果是转办，更新执行人
            if (BpmTaskExe.TYPE_TRANSMIT.equals(assignType)) {
                SysUser sysUser = (SysUser) ContextUtil.getCurrentUser();
                taskOpinion.setExeUserId(sysUser.getUserId());
                taskOpinion.setExeFullname(sysUser.getFullName());
            }
            taskOpinion.setOpinion(bpmTaskExe.getMemo());
            taskOpinion.setEndTime(new Date());
            //时间间隔计算
            Long duration = taskOpinion.getEndTime().getTime() - taskOpinion.getStartTime().getTime();
            taskOpinion.setDurTime(duration);
            taskOpinionService.update(taskOpinion);

            //新添加一个任务意见，状态为初始值
            TaskOpinion newOpinion = new TaskOpinion();
            newOpinion.setOpinionId(UUIDUtils.getUUIDFor32());
            newOpinion.setProcInstId(processRun.getProcInstId());
            newOpinion.setProcDefId(processRun.getProcDefId());
            newOpinion.setCheckStatus(TaskOpinion.STATUS_CHECKING);
            newOpinion.setStartTime(new Date());
            newOpinion.setTaskKey(bpmTaskExe.getTaskDefKey());
            newOpinion.setTaskName(bpmTaskExe.getTaskName());
            newOpinion.setTaskId(taskId);
            newOpinion.setExeUserId(assigneeId);
            newOpinion.setExeFullname(bpmTaskExe.getAssigneeName());

            TaskEntity taskEntity = bpmService.getTask(taskId);
            this.dealTaskOpinSupId(taskEntity, newOpinion);

            taskOpinionService.add(newOpinion);

            //记录日志
            StringBuffer memo = new StringBuffer("流程:" + processRun.getSubject() + ", 【" + bpmTaskExe.getOwnerName() + "】将任务【" + taskEntity.getName() + "】");
            if (BpmTaskExe.TYPE_ASSIGNEE.equals(assignType)) {
                memo.append( "代理给【" + bpmTaskExe.getAssigneeName() + "】执行。");
                bpmRunLogService.addRunLog(processRun.getRunId(), BpmRunLog.OPERATOR_TYPE_AGENT, memo.toString());
            }else{
                memo.append("转发给【" + bpmTaskExe.getAssigneeName() + "】执行。");
                bpmRunLogService.addRunLog(processRun.getRunId(), BpmRunLog.OPERATOR_TYPE_DELEGATE, memo.toString());
            }

            //发送转办/代理人消息
            this.sendMessage(bpmTaskExe, bpmTaskExe.getInformType(), useType, agentUseType, bpmTaskExe.getMemo(), false);
        }catch (Exception ex){
            throw ex;
        }

    }

    /**
     * 发送消息
     * @param bpmTaskExe
     * @param informType
     * @param useType
     * @param agentUseType
     * @param opinion
     * @param cancel
     */
    private void sendMessage(BpmTaskExe bpmTaskExe, String informType, Integer useType, Integer agentUseType, String opinion, boolean cancel) throws Exception {
//        取消时设置任务id为空
        String taskId = cancel?null:bpmTaskExe.getTaskId();

        Map<String,String> msgTempMap = sysTemplateService.getTempByFun(useType);

        String receiverId = bpmTaskExe.getAssigneeId();
        SysUser assignUser= sysUserService.getById(receiverId);
        List<SysUser> receiverUserList = new ArrayList<SysUser>();
        receiverUserList.add(assignUser);
        SysUser curUser = (SysUser)ContextUtil.getCurrentUser();
        taskMessageService.sendMessage(curUser,receiverUserList,informType,msgTempMap,bpmTaskExe.getSubject(),opinion,taskId,null,null);

        //处理通知任务所属人(代理)的消息
        if (agentUseType != 0) {
            Map<String, String> msgAgeTempMap = sysTemplateService.getTempByFun(agentUseType);
            String owerId = bpmTaskExe.getOwnerId();
            SysUser owerUser = sysUserService.getById(owerId);
            List<SysUser> receiverAgeUserList=new ArrayList<SysUser>();
            receiverAgeUserList.add(owerUser);
            taskMessageService.sendMessage(curUser, receiverAgeUserList, informType, msgAgeTempMap, bpmTaskExe.getSubject(), opinion, taskId, null,null);
        }
    }

    /**
     * 处理审批意见是否添加父流程，如果是外部子流程则添加，否则不添加
     * @param taskEntity
     * @param taskOpinion
     */
    private void dealTaskOpinSupId(TaskEntity taskEntity, TaskOpinion taskOpinion) {
        if(BeanUtils.isEmpty(taskEntity)){ return; }
        //加签会签时，获取流转的主任务
        while (taskEntity.getExecutionId()==null){
            String parentTaskId = taskEntity.getParentTaskId();
            taskEntity = bpmService.getTask(parentTaskId);
        }
        String superProcInstId = processRunService.getSuperProcInstId(taskEntity.getProcessInstanceId());

        taskOpinion.setSuperExecution(superProcInstId);

        //外部子流程设置流程实例
        taskOpinion.setProcInstId(taskEntity.getProcessInstanceId());
        taskOpinion.setProcDefId(taskEntity.getProcessDefinitionId());
    }

    /**
     * 判断该节点是否可以使用转发代办
     * @param taskEntity
     * @param actDefModel
     */
    public boolean isAssigneeTask(TaskEntity taskEntity, ActDefModel actDefModel) {
        try {
            //第一个节点不能转办
            boolean isFirstNode = NodeCache.isFirstNode(taskEntity.getProcessDefinitionId(),taskEntity.getTaskDefinitionKey());
            if(isFirstNode){
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //流程定义设置—其他参数—是否允许转办的值
        boolean rtn = actDefModel.getAllowDivert()==1;
        if(!rtn){
            return false;
        }
        //2019-11-21 新增逻辑：1、任务在驳回和已转办的状态下，不再出现交办按钮
        //驳回、已经转办的任务不能在转办
        return this.isCanAssignee(taskEntity.getDescription());
    }

    /**
     * 判断任务状态是否能交办。
     * @param taskStatus
     * @return
     */
    public boolean isCanAssignee(String taskStatus) {
        //2019-11-21 新增逻辑：若任务被驳回或撤销到发起人，则属于第一节点不能转办的情况，故去除该两种情况的if判断
        // =21，已经转办；=3，驳回
        if(TaskOpinion.STATUS_REJECT.toString().equals(taskStatus)||
           TaskOpinion.STATUS_DELEGATE.toString().equals(taskStatus)){
                return false;
        }
        return true;
    }

    /**
     * 在转办的情况下，进行多级取消转办
     * @param bpmTaskExe
     * @param sysUser
     * @param opinion
     * @param informType
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelBacth(BpmTaskExe bpmTaskExe, SysUser sysUser, String opinion, String informType) throws Exception {
        if(BpmTaskExe.TYPE_TRANSMIT.equals(bpmTaskExe.getAssignType())){
            cancelMutilLevel(bpmTaskExe, sysUser, opinion, informType);
        }else {
            doCancel(bpmTaskExe, sysUser, opinion, informType);
        }
    }
    /**
     * 批量取消转办
     * @param ids
     * @param sysUser
     * @param opinion
     * @param informType
     */
    public List<BpmTaskExe> cancelBacths(String ids, SysUser sysUser, String opinion, String informType) throws Exception {
        String[] aryId = ids.split(",");
        List<BpmTaskExe> list = new ArrayList<BpmTaskExe>();
        for (int i = 0; i < aryId.length; i++) {
            String id = aryId[i];
            BpmTaskExe bpmTaskExe = bpmTaskExeDao.getById(id);
            String  taskId = bpmTaskExe.getTaskId();
            TaskEntity taskEntity = bpmService.getTask(taskId);
            if (taskEntity == null) {
                //记录将不执行取消的任务,并将其转发代理类型状态进行修改
                this.cancelOver(id);
                MessageUtil.addMsg(bpmTaskExe.getSubject()+",");
                continue;
            }
            cancelBacth(bpmTaskExe, sysUser, opinion, informType);
            list.add(bpmTaskExe);
        }
        return list;

    }

    private void cancelMutilLevel(BpmTaskExe bpmTaskExe, SysUser sysUser, String opinion, String informType) throws Exception {
        // 查看是否还有下级转办
        Map<String, Object> params = new HashMap<String, Object>() ;
        if(StringUtil.isNotEmpty(opinion)){
            params.put("opinion", opinion);
        }
        if(StringUtil.isNotEmpty(informType)){
            params.put("informType", informType);
        }
        params.put("id", bpmTaskExe.getId());
        params.put("ownerId", bpmTaskExe.getAssigneeId());
        params.put("taskId", bpmTaskExe.getTaskId());
        params.put("status", BpmTaskExe.STATUS_INIT);
        List<BpmTaskExe> taskList = bpmTaskExeDao.queryList(params);
        if(BeanUtils.isNotEmpty(taskList)){
            // 继续对下级转办进行取消操作
            SysUser user = sysUserService.getById(bpmTaskExe.getAssigneeId()) ;
            cancelMutilLevel(taskList.get(0), user, opinion, informType);
        }
        doCancel(bpmTaskExe, sysUser, opinion, informType);
    }
    private ProcessRun doCancel(BpmTaskExe bpmTaskExe, SysUser sysUser, String opinion, String informType) throws Exception {
        // 在转办任务中该记录标记为取消
        Short opinionStatus  = TaskOpinion.STATUS_CHECKING;
        ProcessRun processRun= processRunDao.getById(bpmTaskExe.getRunId());
        SysUser curUser = (SysUser)ContextUtil.getCurrentUser();

        String memo = bpmTaskExe.getMemo() + ",取消了该任务原因:" + opinion;
        bpmTaskExe.setMemo(memo);
        bpmTaskExe.setExeUserId(bpmTaskExe.getOwnerId());
        bpmTaskExe.setExeUserName(bpmTaskExe.getOwnerName());
        bpmTaskExe.setStatus(BpmTaskExe.STATUS_CANCEL);
        bpmTaskExe.setExeTime(new Date());
        bpmTaskExeDao.update(bpmTaskExe);

        // 设置任务执行人为本人
        Map<String,Object> params = new  HashMap<String,Object>(3);
        params.put("taskId",bpmTaskExe.getTaskId());
        params.put("userId",sysUser.getUserId());
        params.put("description", opinionStatus.toString());
        params.put("updateTime", new Date());
        taskDao.updateTask(params);

        //获取任务意见并进行修改
        // 获取任务意见并进行修改。
        TaskOpinion taskOpinion = taskOpinionService.getByTaskId(bpmTaskExe.getTaskId());
        // 状态修改为取消。
        taskOpinion.setCheckStatus(TaskOpinion.STATUS_DELEGATE_CANCEL);
        taskOpinion.setExeUserId(curUser.getUserId());
        taskOpinion.setExeFullname(curUser.getFullName());
        taskOpinion.setOpinion(opinion);
        taskOpinion.setEndTime(BeanUtils.isEmpty(taskOpinion.getEndTime())?new Date():taskOpinion.getEndTime());
        //时间间隔处理
        Long duration =taskOpinion.getEndTime().getTime()-taskOpinion.getStartTime().getTime();
        taskOpinion.setDurTime(duration);
        taskOpinion.setEndTime(new Date());
        taskOpinionService.update(taskOpinion);

        // 新添加一个任务意见，状态为初始值。
        TaskOpinion newOpinion = new TaskOpinion();
        newOpinion.setOpinionId(UUIDUtils.getUUIDFor32());
        newOpinion.setProcInstId(processRun.getProcInstId());
        newOpinion.setProcDefId(processRun.getProcDefId());
        newOpinion.setCheckStatus(TaskOpinion.STATUS_CHECKING);
        newOpinion.setStartTime(new Date());
        newOpinion.setTaskKey(bpmTaskExe.getTaskDefKey());
        newOpinion.setTaskName(bpmTaskExe.getTaskName());
        newOpinion.setTaskId(bpmTaskExe.getTaskId());
        newOpinion.setExeUserId(bpmTaskExe.getOwnerId());
        newOpinion.setExeFullname(bpmTaskExe.getOwnerName());

        TaskEntity taskEntity=bpmService.getTask(bpmTaskExe.getId().toString());
        this.dealTaskOpinSupId(taskEntity, newOpinion);

        taskOpinionService.add(newOpinion);

        Integer userType = SysTemplate.USE_TYPE_CANCLE_DELEGATE;
        if (BpmTaskExe.TYPE_ASSIGNEE.equals(bpmTaskExe.getAssignType())) {
            userType = SysTemplate.USE_TYPE_CANCLE_AGENT;
        } else if (BpmTaskExe.TYPE_TRANSMIT.equals(bpmTaskExe.getAssignType())) {
            userType = SysTemplate.USE_TYPE_CANCLE_DELEGATE;
        }

        //转办人通知被转办人
        this.sendMessage(bpmTaskExe, informType,userType,0, opinion,true);

        return processRun;

    }

    /**
     * 将任务已结束的转办代理流程的转办（代理）类型status变为3（其他）
     * @param id
     */
    public void cancelOver(String id) {
        bpmTaskExeDao.updateStatus(id);
    }
}
